package com.jddai.arithmetic;

import lombok.Data;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

@Data
public class PSO_Data {

    private double c1 = 2;
    private double c2 = 0;
    private double weight = 0.8;

    private int maxgen = 200;
    private int sizepop = 30;

    private ArrayList<ArrayList<Double>> BTR_Model;
    private ArrayList<ArrayList<Double>> Stl_Model;

    private ArrayList<Double> Data;

    private ArrayList<Double> BTR_uscale;
    private ArrayList<Double> Stl_uscale;

    //private double[][] Pareto = new double[100][2]; //第一个值存放BTR， 第二个值存放Stl
    private ArrayList<double[]> Pareto = new ArrayList<>();

    private ArrayList<ArrayList<Double>> pareto_pop = new ArrayList<>();


    private ArrayList<Double> ub = new ArrayList<>();
    private ArrayList<Double> lb = new ArrayList<>();

    private ArrayList<Double> Vmax = new ArrayList<>();
    private ArrayList<Double> Vmin = new ArrayList<>();

    private ArrayList<ArrayList<Double>> pop = new ArrayList<>();
    private ArrayList<ArrayList<Double>> V = new ArrayList<>();

    private ArrayList<Integer> attrArray;

    private double[][] pbestfitness = new double[sizepop][2]; //个体最佳
    private ArrayList<ArrayList<Double>> pbest = new ArrayList<>();

    private double[] TopsisFitness = new double[2];
    private ArrayList<Double> TopsisPop = new ArrayList<>();

    public PSO_Data(ArrayList<ArrayList<Double>> BTR_Model, ArrayList<ArrayList<Double>> stl_Model, ArrayList<Double> data, ArrayList<Double> BTR_uscale, ArrayList<Double> stl_uscale, ArrayList<Integer> attrArray) {
        this.BTR_Model = BTR_Model;
        this.Stl_Model = stl_Model;
        this.Data = data;
        this.BTR_uscale = BTR_uscale;
        this.Stl_uscale = stl_uscale;
        this.attrArray = attrArray;
    }

    public Double BTR_fitness(ArrayList<Double> single_pop) throws IOException {
        Double Fitness;
        ArrayList<ArrayList<Double>> data_temp = new ArrayList<>();
        data_temp.add(single_pop);

        BRBInference BRB = new BRBInference(BTR_uscale, data_temp, BTR_Model);
        BRB.attr_identify();
        BRB.fuse();

        Fitness = BRBInference.abs(BRB.getInference_Result().get(0));

        return Fitness;
    }

    public Double Stl_fitness(ArrayList<Double> single_pop) throws IOException {
        Double Fitness;
        ArrayList<ArrayList<Double>> data_temp = new ArrayList<>();
        data_temp.add(single_pop);

        BRBInference BRB = new BRBInference(Stl_uscale, data_temp, Stl_Model);
        BRB.attr_identify();
        BRB.fuse();

        Fitness = BRBInference.abs(BRB.getInference_Result().get(0));

        return Fitness;
    }

    public void updatePareto(int num, ArrayList<Double> single_pop) throws IOException {

        Double btr = BTR_fitness(single_pop);
        Double stl = Stl_fitness(single_pop);


        while (Pareto.size() > 100) {
            double tmp = new Random().nextDouble();
            int index = (int) tmp * 100;
            Pareto.remove(index);
        }

        /*for(int i=0; i<Pareto.size(); i++){
            if(btr - Pareto.get(i)[0] < 0.0001 && stl - Pareto.get(i)[1] < 0.001){
                return;
            }
        }*/

        Pareto.add(new double[]{btr, stl});
        pareto_pop.add(single_pop);

        ArrayList<double[]> ParetoSet = new ArrayList<>();
        ArrayList<ArrayList<Double>> PopSet = new ArrayList<>();

        ParetoSet.add(Pareto.get(0));
        PopSet.add(pareto_pop.get(0));

        for (int i = 1; i < Pareto.size(); i++) {
            boolean flag2 = true;
            for (int j = 0; j < ParetoSet.size(); j++) {
                if ((Pareto.get(i)[0] > ParetoSet.get(j)[0] && Pareto.get(i)[1] > ParetoSet.get(j)[1])) {
                    if (!flag2) {//可能已经加入ParetoSet了
                        ParetoSet.remove(ParetoSet.size() - 1);
                        PopSet.remove(PopSet.size() - 1);
                    }
                    break;
                }
                if ((Pareto.get(i)[0] < ParetoSet.get(j)[0] && Pareto.get(i)[1] < ParetoSet.get(j)[1])) {
                    if (flag2) {
                        ParetoSet.add(new double[]{Pareto.get(i)[0], Pareto.get(i)[1]});
                        PopSet.add(pareto_pop.get(i));
                        flag2 = false;
                    }
                    ParetoSet.remove(j);
                    PopSet.remove(j);
                    j = j - 1;
                    continue;
                }
                if (flag2 && (Pareto.get(i)[0] < ParetoSet.get(j)[0] || Pareto.get(i)[1] < ParetoSet.get(j)[1])) {
                    ParetoSet.add(new double[]{Pareto.get(i)[0], Pareto.get(i)[1]});
                    PopSet.add(pareto_pop.get(i));
                    flag2 = false;
                }
            }
        }

        Pareto = ParetoSet;
        pareto_pop = PopSet;

        double square = Math.pow(btr, 2) + Math.pow(stl, 2);
        double temp = Math.pow(pbestfitness[num][0], 2) + Math.pow(pbestfitness[num][1], 2);
        if (square < temp) {
            pbestfitness[num][0] = btr;
            pbestfitness[num][1] = stl;
            pbest.set(num, single_pop);
        }
    }

    public void Init() throws IOException {

        for (int i = 0; i < attrArray.size(); i++) {
            attrArray.set(i, attrArray.get(i) - 1);
        }

        for (int i = 1; i <= Data.size(); ++i) {
            ub.add(BTR_Model.get(BTR_Model.size() - 1).get(i));
            lb.add(BTR_Model.get(0).get(i));
        }

        for (int i = 1; i < ub.size(); ++i) {
            Vmax.add((ub.get(i) - lb.get(i)) * 0.5);
            Vmin.add(-(ub.get(i) - lb.get(i)) * 0.5);
        }

        for (int k = 0; k < sizepop; ++k) {
            ArrayList<Double> Temp = new ArrayList<>();
            for (Double value : Data) {
                Temp.add(value);
            }
            Temp.add(-7.67);
            pop.add(Temp);
        }

        for (int k = 0; k < sizepop; k++) {
            ArrayList<Double> v = new ArrayList<>();
            for (int i = 0; i < attrArray.size(); ++i) {
                Double ub_temp = ub.get(attrArray.get(i));
                Double lb_temp = lb.get(attrArray.get(i));
                double tmp = new Random().nextDouble();
                if (k != 0) {
                    double temp = lb_temp + (ub_temp - lb_temp) * tmp;
                    pop.get(k).set(attrArray.get(i), temp);
                }
                v.add((ub_temp - lb_temp) * tmp * 0.5);
            }
            V.add(v);
            Double btr_fitness = BTR_fitness(pop.get(k));
            Double stl_fitness = Stl_fitness(pop.get(k));

            if (k < 45) {
                double[] temp = new double[]{btr_fitness, stl_fitness};
                Pareto.add(temp);
                pareto_pop.add(pop.get(k));
            } else {
                updatePareto(k, pop.get(k));
            }

            pbestfitness[k][0] = btr_fitness;
            pbestfitness[k][1] = stl_fitness;
            pbest.add(pop.get(k));
        }

    }

    public void Process() throws IOException {

        for (int i = 0; i < maxgen; i++) {
         /*   if (i == 0) {
                for (int j = 0; j < Pareto.size(); j++) {
                    System.out.print(Pareto.get(j)[0] + "\t" + Pareto.get(j)[1] + "\n");
                }
                System.out.println(0.0 + "\t" + 0.0 + "\n");
            }
            if (i == 5) {
                for (int j = 0; j < Pareto.size(); j++) {
                    System.out.print(Pareto.get(j)[0] + "\t" + Pareto.get(j)[1] + "\n");
                }
                System.out.println(0.0 + "\t" + 0.0 + "\n");
            }
            if (i == 10) {
                for (int j = 0; j < Pareto.size(); j++) {
                    System.out.print(Pareto.get(j)[0] + "\t" + Pareto.get(j)[1] + "\n");
                }
                System.out.println(0.0 + "\t" + 0.0 + "\n");
            }
            if (i == 15) {
                for (int j = 0; j < Pareto.size(); j++) {
                    System.out.print(Pareto.get(j)[0] + "\t" + Pareto.get(j)[1] + "\n");
                }
                System.out.println(0.0 + "\t" + 0.0 + "\n");
            }*/

            for (int j = 0; j < sizepop; j++) {
                for (int z = 0; z < attrArray.size(); z++) {
                    double sum = 0.0;
                    for (int x = 0; x < pareto_pop.size(); x++) {
                        sum += pareto_pop.get(x).get(z);
                    }
                    double avg = sum / pareto_pop.size();

                    double tmp = new Random().nextDouble();
                    double v = V.get(j).get(z) * weight +
                            c1 * tmp * (avg - pop.get(j).get(attrArray.get(z))) +
                            c2 * tmp * (pbest.get(j).get(attrArray.get(z)) - pop.get(j).get(attrArray.get(z)));

                    if (v > Vmax.get(attrArray.get(z))) {
                        v = Vmax.get(attrArray.get(z));
                    }
                    if (v < Vmin.get(attrArray.get(z))) {
                        v = Vmin.get(attrArray.get(z));
                    }
                    V.get(j).set(z, v);
                    double temp = pop.get(j).get(attrArray.get(z)) + v;
                    if (temp > ub.get(attrArray.get(z))) {
                        temp = ub.get(attrArray.get(z));
                    }

                    if (temp < lb.get(attrArray.get(z))) {
                        temp = lb.get(attrArray.get(z));
                    }

                    pop.get(j).set(attrArray.get(z), temp);

                }
                updatePareto(j, pop.get(j));
            }

        }

    }

    public void Topsis() {

        double BTR_max = Pareto.get(0)[0];
        double BTR_min = Pareto.get(0)[0];

        double Stl_max = Pareto.get(0)[1];
        double Stl_min = Pareto.get(0)[1];

        for (double[] value : Pareto) {
            if (value[0] > BTR_max) {
                BTR_max = value[0];
            }
            if (value[0] < BTR_min) {
                BTR_min = value[0];
            }
            if (value[1] > Stl_max) {
                Stl_max = value[1];
            }
            if (value[1] < Stl_min) {
                Stl_min = value[1];
            }
        }
        double topsisMin = 0;
        int index = 0;
        for (int i = 0; i < Pareto.size(); i++) {
            double dpos = Math.pow(Pareto.get(i)[0] - BTR_min, 2) + Math.pow(Pareto.get(i)[1] - Stl_min, 2);
            double dneg = Math.pow(Pareto.get(i)[0] - BTR_max, 2) + Math.pow(Pareto.get(i)[1] - Stl_max, 2);
            double value = dpos / (dpos + dneg);
            if(i == 0){
                topsisMin =value;
            }else {
             if (value < topsisMin){
                 topsisMin = value;
                 index = i;
             }
            }
        }

        TopsisFitness = Pareto.get(index);
        TopsisPop = pareto_pop.get(index);

    }

}
